package com.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.system.constant.CommonConstant;
import com.system.constant.HTTPStatus;
import com.system.entity.Menu;
import com.system.entity.RoleMenu;
import com.system.entity.Teacher;
import com.system.mapper.MenuMapper;
import com.system.mapper.RoleMenuMapper;
import com.system.response.Result;
import com.system.response.menu.MenuDtoResponse;
import com.system.service.MenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.system.service.RoleService;
import com.system.response.vo.MetaVo;
import com.system.response.vo.RouterVo;
import com.system.service.TeacherService;
import com.system.util.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * <p>
 * 菜单表 服务实现类
 * </p>
 *
 * @author zfc
 * @since 2023-04-20
 */
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private RoleService roleService;

    @Autowired
    private TeacherService teacherService;

    @Override
    public Result selectMenuTreeByRoleNames() {
        //获取当前用户的Subject对象（例如获取当前用户的身份信息等）
        Subject subject = SecurityUtils.getSubject();
        String username = (String) subject.getPrincipal();

        //通过用户名查询用户
        Teacher teacher = teacherService.getTeacherByUserName(username);
        if (teacher == null) return new Result().setCode(HTTPStatus.UNAUTHORIZED).setMessage("请重新登录后重试！！！");

        //查询所有角色名，通过用户名
        Set<String> roleNames = roleService.selectAllRoleNamesByUsername(username);
        //通过角色名查询菜单
        List<Menu> menus = menuMapper.selectMenuTreeByRoleNames(roleNames);
        //根据父菜单获取子菜单
        List<Menu> menuList = getChildPerms(menus, 0);
        //构建菜单
        List<RouterVo> menuTree = this.buildMenus(menuList);
        return new Result().setCode(HTTPStatus.SUCCESS).setData(menuTree);
    }


    @Override
    public List<RouterVo> buildMenus(List<Menu> menus) {
        List<RouterVo> routers = new LinkedList<RouterVo>();

        for (Menu menu : menus) {
            RouterVo router = new RouterVo();
            router.setName(menu.getMenuName());
            router.setComponent(menu.getComponent());
            router.setPath(menu.getPath());
            router.setMeta(new MetaVo(menu.getId(), menu.getMenuName(), menu.getIcon(), menu.getCreateDate(), menu.getStatus(), menu.getSort(), menu.getPerms()));

            List<Menu> cMenus = menu.getChildren();

            if (StringUtils.isNotEmpty(cMenus) && menu.getType().equals("M")) {
                router.setChildren(buildMenus(cMenus));
            }

            routers.add(router);
        }
        return routers;
    }

    @Override
    public Result getAllMenu() {
        List<Menu> menus = menuMapper.selectList(
                new LambdaQueryWrapper<Menu>()
                        .eq(Menu::getStatus, CommonConstant.STATUS_NORMAL)
                        .orderByAsc(Menu::getSort)
        );
        List<Menu> menuList = getChildPerms(menus, 0);

        return new Result().setCode(HTTPStatus.SUCCESS).setData(menuList);
    }

    public static int iii = 0;//这个条件只有在查看菜单列表状态是：1停用的情况下触发。

    @Override
    public List<Menu> getAllMenuTree(MenuDtoResponse menuDtoResponse) {

        List<Menu> menus = menuMapper.selectList(
                new LambdaQueryWrapper<Menu>()
                        .eq(!StringUtils.isNull(menuDtoResponse.getStatus()), Menu::getStatus, menuDtoResponse.getStatus())
                        .like(!StringUtils.isNull(menuDtoResponse.getMenuName()), Menu::getMenuName, menuDtoResponse.getMenuName())
                        .orderByAsc(Menu::getSort)
        );

        //暂存通过父id遍历出来的数据
        List<List<Menu>> menuList = new ArrayList<List<Menu>>();

        //判1、断数据状态，如果有一条状态为0，则代表所有的都是停用
        if (menus.size() > 0 && Integer.valueOf(menus.get(0).getStatus().trim()).equals(CommonConstant.STATUS_BLOCK)) {
            //2、遍历数据库中获取的数据
            for (int i = 0; i < menus.size(); i++) {
                iii = 0;//控制是否加入暂存list
                Menu menu = menus.get(i);

                for (int i1 = 0; i1 < menuList.size(); i1++) {
                    List<Menu> list = menuList.get(i1);
                    for (int i2 = 0; i2 < list.size(); i2++) {
                        recursionChildren(list, menu.getId());
                    }
                    if (iii == 2) {
                        //2：删除 暂存通过父id遍历出来的数据，因为需要遍历的是他的父菜单，如果不删除，会重复展示
                        menuList.remove(i1);
                    }
                }
                //3、通过父id遍历出tree，并存入list中
                if (iii != 1) {
                    menuList.add(getChildPerms(menus, menu.getParentId()));
                }
            }

            //创建新的list，将 暂存通过父id遍历出来的数据 解析出来返回
            List<Menu> newList = new ArrayList<>();
            for (int i = 0; i < menuList.size(); i++) {
                List<Menu> list = menuList.get(i);
                for (int i1 = 0; i1 < list.size(); i1++) {
                    newList.add(list.get(i1));
                }
            }

            //4、返回遍历出的数据
            return newList;
        }
        return getChildPerms(menus, 0);
    }

    /**
     * 递归菜单，查看是否有重复的菜单
     *
     * @param menuList 菜单列表
     * @param id       需要查看是否重复的id
     * @return 0：不管
     * 1：代表菜单列表中已经有了，不存入
     * 2：代表这个id是当前菜单列表的父菜单，需要删除当前菜单
     */
    public static int recursionChildren(List<Menu> menuList, Integer id) {
        for (Menu menu : menuList) {
            //如果暂存空间中有数据的id，和需要存入的数据id一直，不存入
            if (menu.getId().equals(id)) {
                return iii = 1;
            }
            //暂存空间中数据的父id和需要存入的数据id
            if (menu.getParentId().equals(id)) {
                return iii = 2;
            }
            if (!StringUtils.isNull(menu.getChildren())) {
                //子菜单不为空，递归
                recursionChildren(menu.getChildren(), id);
            }
        }
        return iii;
    }


    @Override
    public boolean addMenu(Menu menu) {

        //新增的字段（所有类型都需要的字段
        Menu newMenu = new Menu();
        newMenu.setParentId(menu.getParentId());//父菜单
        newMenu.setType(menu.getType());//菜单类型
        newMenu.setMenuName(menu.getMenuName());//菜单名称
        newMenu.setSort(menu.getSort());//显示顺序
        //目录 || 菜单
        if (menu.getType().equals(CommonConstant.TYPE_DIR) || menu.getType().equals(CommonConstant.TYPE_MENU)) {
            newMenu.setIcon(menu.getIcon());//菜单图标
            newMenu.setPath(menu.getPath());//路由地址
            newMenu.setStatus(menu.getStatus());//菜单状态
            if (menu.getType().equals(CommonConstant.TYPE_MENU)) {
                newMenu.setComponent(menu.getComponent());//组建路径
                newMenu.setPerms(menu.getPerms());//权限字符
            }
        } else {
            //代表是按钮
            newMenu.setPerms(menu.getPerms());//权限字符
        }

        return menuMapper.insert(newMenu) > 0;
    }

    @Override
    public boolean updateMenu(Menu menu) {

        return menuMapper.updateById(menu) > 0;
    }

    @Override
    public Menu selectMenuById(Integer id) {
        return menuMapper.selectOne(
                new LambdaQueryWrapper<Menu>()
                        .eq(Menu::getId, id)
        );
    }

    @Override
    public boolean recoverMenu(Menu menu) {
        return menuMapper.updateById(menu) > 0;
    }

    @Override
    public boolean completelyDeleteMenuById(Integer id) {
        return menuMapper.deleteById(id) > 0;
    }

    @Override
    public Menu selectMenuAllById(Integer id) {
        return menuMapper.selectById(id);
    }

    @Override
    public int checkParentMenuHaveChildMenu(Integer id) {
        return menuMapper.selectCount(
                new LambdaQueryWrapper<Menu>()
                        .eq(Menu::getParentId, id)
                        .eq(Menu::getStatus, 0)
        );
    }

    @Override
    public boolean checkMenuNameUnique(Menu menu) {
        Menu info = menuMapper.selectOne(
                new LambdaQueryWrapper<Menu>()
                        .eq(Menu::getMenuName, menu.getMenuName())
                        .eq(!StringUtils.isNull(menu.getParentId()), Menu::getParentId, menu.getParentId())
        );

        if (!StringUtils.isNull(info)) {
            if (menu.getMenuName().equals(info.getMenuName()) && menu.getId().equals(info.getId())) {
                return CommonConstant.UNIQUE;
            }
            return CommonConstant.NOT_UNIQUE;
        }

        return CommonConstant.UNIQUE;
    }

    @Override
    public int checkParentMenuExistById(Integer id) {
        return menuMapper.selectCount(
                new LambdaQueryWrapper<Menu>()
                        .eq(Menu::getId, id)
        );
    }

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Override
    public List<Menu> queryRolesBoundMenus(Integer roleId) {
        List<RoleMenu> roleMenuList = roleMenuMapper.selectList(
                new LambdaQueryWrapper<RoleMenu>()
                        .eq(RoleMenu::getRoleId, roleId)
        );
        if (roleMenuList.size() == 0) return null;

        Integer[] menuIds = new Integer[roleMenuList.size()];
        for (int i = 0; i < roleMenuList.size(); i++) {
            menuIds[i] = roleMenuList.get(i).getMenuId();
        }
        List<Menu> menuList = menuMapper.selectList(
                new LambdaQueryWrapper<Menu>()
                        .in(Menu::getId, menuIds)
                        .eq(Menu::getStatus, CommonConstant.STATUS_NORMAL)
                        .orderByAsc(Menu::getSort)
        );
        return menuList;
    }

    @Transactional(rollbackFor = Exception.class, timeout = 1000 * 60)
    @Override
    public boolean roleBoundMenu(Integer roleId, Integer[] menuIds) {
        for (Integer menuId : menuIds) {
            RoleMenu rm = new RoleMenu();
            rm.setRoleId(roleId);
            rm.setMenuId(menuId);
            //查询是否存在，不能重复绑定
            Integer integer = roleMenuMapper.selectCount(new LambdaQueryWrapper<RoleMenu>()
                    .eq(RoleMenu::getMenuId, menuId)
                    .eq(RoleMenu::getRoleId, roleId));
            if (integer==0){
                int insert = roleMenuMapper.insert(rm);
                if (insert <= 0) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public boolean checkRoleBoundMenuUnique(Integer roleId, Integer[] menuIds) {
        List<RoleMenu> roleMenuList = roleMenuMapper.selectList(
                new LambdaQueryWrapper<RoleMenu>()
                        .eq(RoleMenu::getRoleId,roleId)
                        .in(RoleMenu::getMenuId, menuIds)
        );
        return roleMenuList.size() == 0;
    }

    @Transactional(rollbackFor = Exception.class, timeout = 1000 * 60)
    @Override
    public boolean batchDeleteRoleBoundMenus(Integer roleId, Integer[] menuIds) {

        for (Integer menuId : menuIds) {
            int delete = roleMenuMapper.delete(
                    new LambdaQueryWrapper<RoleMenu>()
                            .eq(RoleMenu::getRoleId, roleId)
                            .eq(RoleMenu::getMenuId, menuId)
            );
            if (delete <= 0) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean deleteRoleBoundMenusById(Integer roleId) {
        return roleMenuMapper.delete(
                new LambdaQueryWrapper<RoleMenu>()
                        .eq(RoleMenu::getRoleId,roleId)
        ) > 0;
    }

    @Override
    public boolean checkRoleBoundMenus(RoleMenu rm) {
        List<RoleMenu> RoleMenus = roleMenuMapper.selectList(
                new LambdaQueryWrapper<RoleMenu>()
                        .eq(!StringUtils.isNull(rm.getRoleId()), RoleMenu::getRoleId, rm.getRoleId())
                        .eq(!StringUtils.isNull(rm.getMenuId()), RoleMenu::getMenuId, rm.getMenuId())
        );
        return RoleMenus.size() > 0;
    }


    /**
     * --------------------------------------------------------------------------------------------------------------
     */

    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list     分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    public List<Menu> getChildPerms(List<Menu> list, int parentId) {
        List<Menu> returnList = new ArrayList<Menu>();
        for (Iterator<Menu> iterator = list.iterator(); iterator.hasNext(); ) {
            Menu t = (Menu) iterator.next();
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (t.getParentId() == parentId) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     *
     * @param list 分类表
     * @param t    子节点
     */
    private void recursionFn(List<Menu> list, Menu t) {
        // 得到子节点列表
        List<Menu> childList = getChildList(list, t);
        t.setChildren(childList);
        for (Menu tChild : childList) {
            if (hasChild(list, tChild)) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
     * 得到子节点列表
     */
    private List<Menu> getChildList(List<Menu> list, Menu t) {
        List<Menu> tlist = new ArrayList<Menu>();
        Iterator<Menu> it = list.iterator();
        while (it.hasNext()) {
            Menu n = (Menu) it.next();
            if (n.getParentId().longValue() == t.getId().longValue()) {
                tlist.add(n);
            }
        }
        return tlist;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChild(List<Menu> list, Menu t) {
        return getChildList(list, t).size() > 0;
    }

}
